package com.geekchique.runningbuddy.service;

import java.util.List;
import java.util.Date;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.os.DeadObjectException;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteCallbackList;
import android.util.Log;
import android.widget.Toast;
import android.content.IntentReceiver;
import android.content.IntentFilter;
import android.location.Location;

import com.geekchique.runningbuddy.R;
import com.geekchique.runningbuddy.activity.Stopwatch;
import com.geekchique.runningbuddy.provider.ActivityID;
import com.geekchique.runningbuddy.util.DBHelperImpl;
import com.geekchique.runningbuddy.model.Jog;

public class RemoteStopClockService extends Service
{
	final RemoteCallbackList<IRemoteServiceCallback> registeredCallbacks  = new RemoteCallbackList<IRemoteServiceCallback>();
    private NotificationManager mNM;
	protected LocationManager locationManager = null;
	private static final String TAG = "[RBuddy]: " + RemoteStopClockService.class.getName();
    protected LocationProvider provider;
    
    private boolean clockRunning = false;
    
    //private Route currentRoute = null;
    private Jog currentJog = null;
    
    private long startTime = 0;
    private long stopTime = 0;
    private DBHelperImpl dbHelper;

    
    //data related to replaying canned runs
    private boolean cannedRunEnabled = false;
    private CannedRunManagerImpl cannedRunMgr;
    
	@Override
    protected void onCreate() {

		mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);

    	Log.w(TAG, "Stopclock service created.");
		
        locationManager =  (LocationManager)getSystemService(Context.LOCATION_SERVICE);
        
        if (locationManager==null) Log.w(TAG, "null locationManager!");
        else Log.w(TAG, "got locationManager.");
        
    	this.registerReceiver(intentReceiver, new IntentFilter(ActivityID.MY_LOCATION_CHANGED_ACTION));
    	dbHelper = new DBHelperImpl(this);
    	
        showNotification();

        cannedRunMgr = new CannedRunManagerImpl(this);
    }

	private final IntentReceiver intentReceiver = new IntentReceiver()
	{

		@Override
		public void onReceiveIntent(Context context, Intent intent) {
			// TODO Auto-generated method stub
			Location loc = (Location)intent.getParcelableExtra("location");
			
			Log.w(TAG, "Got Location Intent in Service " + loc.getLatitude() + ", " + loc.getLongitude());
			
			if (!cannedRunEnabled)
			{
				Message msg = Message.obtain(messageHandler, ActivityID.LOCATION_UPDATE, loc);
				messageHandler.sendMessageDelayed(msg, 50);
			}

		}
		
	};
	
	
    private final Handler messageHandler = new Handler() {

		@Override
    	public void handleMessage(Message msg) {

        	switch (msg.what) {
        		case ActivityID.STARTSTOPCLOCK:
        	    	Log.w(TAG, "Stopclock service received request to start 1.");
        	        messageHandler.sendEmptyMessage(ActivityID.UPDATESTOPCLOCK);
        			break;
        			
        		case ActivityID.STOPSTOPCLOCK:
        	    	Log.w(TAG, "Stopclock service received request to stop 1.");

        			break;

        		case ActivityID.LOCATION_UPDATE:
        			// Notify the client of the update
        			final int registered = registeredCallbacks.beginBroadcast();
                    Location loc = (Location)msg.obj;
                    for (int i=0; i<registered; i++) {
                    	try {
                        	registeredCallbacks.getBroadcastItem(i).updatedRoute(loc);
                        } catch (DeadObjectException e) {
                        	Log.e(TAG, "Remote service recieved dead object.");
                        }
                    }

                    registeredCallbacks.finishBroadcast();
                    if (currentJog != null)
                    {
                    	currentJog.addRoutePoint(loc);
                    }
                    
        			break;
        			
        		default:
                    super.handleMessage(msg);
            }
        }
    };

    
    /*
	 * IRemoteStopClockServiceInterface is defined through AIDL
	 */
	private final IRemoteStopClockService.Stub messageBinder = new IRemoteStopClockService.Stub() {
		public void registerStopClockUpdateListener(IRemoteServiceCallback cb) throws DeadObjectException {
			if (cb != null) {
				registeredCallbacks.register(cb);
			}
		}

		public void unregisterStopClockUpdateListener(IRemoteServiceCallback cb) throws DeadObjectException {
			if (cb != null) {
				registeredCallbacks.unregister(cb);
			}
		}
		
		public void terminateServiceIfIdle() throws DeadObjectException {
	    	Log.w(TAG, "service received terminateIfIdle request.");
	    	//Give client a second to unbind interface.  This prevents exceptions when service disappears while client
	    	//still has a connection.
	    	new Thread()
	    	{
	    		public void run()
	    		{
	    			try
	    			{
	    				sleep(1000);
	    			}
	    			catch (InterruptedException e)
	    			{
	    				//don't care
	    			}
	    			if (clockRunning == false)
	    			{
	    				Log.w(TAG, "Stopclock service terminating self.");
	    				RemoteStopClockService.this.stopSelf();
	    			}
	    		}
	    	}.start();
		}
		
		public void startStopClock(long startTime) throws DeadObjectException {
			clockRunning = true;
			RemoteStopClockService.this.startTime = startTime;
			currentJog = new Jog(dbHelper, "jog" + new Date().getTime(), startTime);
			
	    	//timerRunnerThread = new StopwatchThread(messageHandler);
			//timerRunnerThread.start();
	    	Log.w(TAG, "Stopclock service received request to start. Created jogId " + currentJog.get_id());
	    	
	    	startTrackingRoute();
		}

		public void stopStopClock(long stopTime) throws DeadObjectException {
			clockRunning = false;
			//timerRunnerThread.stop();
	    	Log.w(TAG, "Stopclock service received request to stop.");
	    	RemoteStopClockService.this.stopTime = stopTime;

	    	stopTrackingRoute();
	    	
			//saveJogLogEntry("jog" + new Date().getTime(), startTime, stopTime);

		}

		
		public long getStartTime() throws DeadObjectException {
			return startTime;
		}

		public long getStopTime() throws DeadObjectException {
			return stopTime;
		}

		public boolean isStopClockRunning() throws DeadObjectException {
			return clockRunning;
		}

		public void resetStopClock() throws DeadObjectException {
		}

		public long getCurrentJogId()
		{
			if (currentJog != null)
			{
				return currentJog.get_id();
			}
			return 0;
		}

		public boolean getCannedRouteEnabled() throws DeadObjectException {
			return cannedRunEnabled;
		}

		public int getCannedRouteId() throws DeadObjectException {
			return cannedRunMgr.getRunId();
		}

		public double getCannedRouteSpeed() throws DeadObjectException {
			return cannedRunMgr.getSpeed();
		}

		public void setCannedRouteEnabled(boolean enabled)
				throws DeadObjectException {
			cannedRunEnabled = enabled;
			
		}

		public void setCannedRouteId(int id) throws DeadObjectException {
			cannedRunMgr.setRunId(id);	
		}

		public void setCannedRouteSpeed(double speed)
				throws DeadObjectException {
			cannedRunMgr.setSpeed(speed);
		}
		
	};


	public void startTrackingRoute() {

		if (!cannedRunEnabled)
		{
			final long MINIMUM_DISTANCECHANGE_FOR_UPDATE = 25; // in Meters
			final long MINIMUM_TIME_BETWEEN_UPDATE = 5000; // in Milliseconds
			List<LocationProvider> locProviders = locationManager.getProviders();
			provider = locProviders.get(0);

			this.locationManager.requestUpdates(provider, MINIMUM_TIME_BETWEEN_UPDATE,	MINIMUM_DISTANCECHANGE_FOR_UPDATE, new Intent(ActivityID.MY_LOCATION_CHANGED_ACTION));
		}
		else
		{
			cannedRunMgr.startRun(messageHandler);
		}
	}


	public void stopTrackingRoute() {
		if (!cannedRunEnabled)
		{
			this.locationManager.removeUpdates(new Intent(ActivityID.MY_LOCATION_CHANGED_ACTION));
		}
		else
		{
			cannedRunMgr.stopRun();
		}
	}


	private void showNotification() {
		Intent contentIntent = new Intent(this, Stopwatch.class);
	    Intent appIntent = new Intent(this, Stopwatch.class);
	    CharSequence text = getText(R.string.local_service_started);

	    mNM.notify(
	    		R.string.local_service_started, // we use a string id because it is a unique
	                                                  // number.  we use it later to cancel the
	                                                  // notification
	               new Notification(
	                   this,                        // our context
	                   R.drawable.stat_sample,                      // the icon for the status bar
	                   text,                  // the text to display in the ticker
	                   System.currentTimeMillis(),  // the timestamp for the notification
	                   getText(R.string.local_service_started),               // the title for the notification
	                   text,                        // the details to display in the notification
	                   contentIntent,               // the contentIntent (see above)
	                   R.drawable.stat_sample,  // the app icon
	                   getText(R.string.local_service_started), // the name of the app
	                   appIntent));                 // the appIntent (see above)
	}

	@Override
	public IBinder onBind(Intent arg0) {
		return messageBinder;
	}

	@Override
	protected void onDestroy() {
	    mNM.cancel(R.string.local_service_started);
		Toast.makeText(this, getText(R.string.local_service_stopped), Toast.LENGTH_SHORT).show();

		this.unregisterReceiver(intentReceiver);

		registeredCallbacks.kill();
		messageHandler.removeMessages(ActivityID.UPDATESTOPCLOCK);
	}
	
/*	
	protected void saveJogLogEntry(String name, long startTime, long stopTime) {

		TreeMap<Long, Location> locationsAndTimes = currentRoute.getlocationsAndTimes();

		dbHelper.addJogEntry(new Jog(name, name, locationsAndTimes));
	}
*/

}

